package com.viish.apps.tripletriad;

import java.util.ArrayList;
import java.util.Random;

import android.util.Log;

import com.viish.apps.tripletriad.carte.Card;
import com.viish.apps.tripletriad.robots.Action;

public class Engine 
{
	public static final int PLAYER = 1;
	public static final int OPPONENT = 2;
	public static final int BOARD_SIZE = 9;
	public static final int MAX_ACTIONS = BOARD_SIZE;
	
	private boolean regleIdentique, reglePlus, regleMemeMur, regleCombo, regleElementaire;
	private int currentPlayer, startingPlayer;
	private Card[] board;
	private String[] boardElements;
	private int nbActionsPlayed;
	private final ArrayList<EventFiredListener> listeners = new ArrayList<EventFiredListener>();
	
	private boolean pvp, isServer;
	
	public Engine(boolean pvp, boolean serveur, boolean identique, boolean plus, boolean mememur, boolean combo, boolean elementaire)
	{
		this.pvp = pvp;
		this.isServer = serveur;
		
		this.regleIdentique = identique;
		this.reglePlus = plus;
		this.regleMemeMur = mememur;
		this.regleCombo = combo;
		this.regleElementaire = elementaire;

		this.nbActionsPlayed = 0;
		this.board = new Card[BOARD_SIZE];
		
		if (!pvp || (pvp && isServer))
		{
			int firstToPlay = new Random().nextInt(2) + 1; // Renvoie une valeur au hasard entre 1 et 2
			this.currentPlayer = firstToPlay;
			this.startingPlayer = firstToPlay;
			if (regleElementaire)
			{
				this.boardElements = new String[BOARD_SIZE];
				initializeElements();
			}
		}
	}
	
	public void onMessageReceived(String m)
	{
		// TODO
	}
	
	public int getStartingPlayer()
	{
		return startingPlayer;
	}
	
	public void addEventFiredListener(EventFiredListener rtl)
	{
		listeners.add(rtl);
	}
	
	public ArrayList<EventFiredListener> getEventFiredListeners() 
	{
        return listeners;
    }
	
	private void fireSameRuleTriggered()
	{
		for(EventFiredListener listener : getEventFiredListeners()) 
		{
            listener.eventSameTriggered();
		}
	}
	
	private void fireSameWallRuleTriggered()
	{
		for(EventFiredListener listener : getEventFiredListeners()) 
		{
            listener.eventSameWallTriggered();
		}
	}
	
	private void firePlusRuleTriggered()
	{
		for(EventFiredListener listener : getEventFiredListeners()) 
		{
            listener.eventPlusTriggered();
		}
	}
	
	private void fireOpponentPlayed()
	{
		for(EventFiredListener listener : getEventFiredListeners()) 
		{
            listener.eventOpponentPlayed(new Action(null, 0, 0)); // TODO : passer le bon objet
		}
	}
	
	private void firePvPGameReadyToStart()
	{
		for(EventFiredListener listener : getEventFiredListeners()) 
		{
            listener.eventPvPGameReadyToStart(null); // TODO : passer le bon objet
		}
	}
	
	private void fireComboRuleTriggered()
	{
		for(EventFiredListener listener : getEventFiredListeners()) 
		{
            listener.eventComboTriggered();
		}
	}

	public int getPlayerScore()
	{
		int player = 0;
		for (Card c : board)
		{
			if (c != null && c.getView().getColor() == PLAYER)
				player++;
		}
		
		if (startingPlayer == OPPONENT) 
			player++;
		return player;
	}

	public int getOpponentScore()
	{
		int opponent = 0;
		for (Card c : board)
		{
			if (c != null && c.getView().getColor() == OPPONENT)
				opponent++;
		}
		
		if (startingPlayer == PLAYER) 
			opponent++;
		return opponent;
	}
	
	private void initializeElements()
	{
		String[] listeElements = {"Earth", "Fire", "Wind", "Holy", "Poison", "Thunder", "Water", "Ice"};
		Random r = new Random();
		boolean caseElementaire = false;
		
		for (int i = 0; i < BOARD_SIZE; i++)
		{
			int rand = r.nextInt(100);
			if (rand < 30 || (i == 5 && !caseElementaire))
			{
				caseElementaire = true;
				int element = r.nextInt(listeElements.length);
				boardElements[i] = listeElements[element];
			}
			else 
			{
				boardElements[i] = "";
			}
		}
	}
	
	public String[] getElements()
	{
		return this.boardElements;
	}
	
	public boolean isBotPlayingFirst()
	{
		return startingPlayer == OPPONENT;
	}
	
	public int getNbToursRestants()
	{
		return MAX_ACTIONS - nbActionsPlayed;
	}
	
	public boolean isGameOver()
	{
		return getNbToursRestants() == 0;
	}
	
	public Card[] getBoard()
	{
		return board;
	}
	
	public boolean isCellEmpty(int cell)
	{
		return board[cell] == null;
	}
	
	public boolean isPlayerTurn()
	{
		return currentPlayer == PLAYER;
	}
	
	public void playCard(int player, Card card, int cell)
	{
		card.lock();
		this.board[cell] = card;
    	this.nbActionsPlayed += 1;
    	
    	if (currentPlayer == PLAYER)
    		currentPlayer = OPPONENT;
		else
			currentPlayer = PLAYER;
    	
		// Applique la Regle Elementaire
		if (regleElementaire) applyElementaireRule(card, cell);
		
    	if (nbActionsPlayed > 1)
    	{
	    	// Applique la Regle Identique
			if (regleIdentique) applySameRule(player, card, cell, false);
			
			// Applique la Regle Plus
			if (reglePlus) applyPlusRule(player, card, cell, false);
			
			// Applique la Regle de base
			applyBasicRule(player, card, cell, false);
    	}
	}
	
	private void applyBasicRule(int player, Card card, int cell, boolean combo)
	{
		int swapped = 0;
		if (cell % 3 == 0) // Carte colonne gauche
		{
			Card c = this.board[cell + 1];
			if (c != null) // Si il y a une carte a sa droite
			{
				if (c.getView().getColor() != player && card.getRightValue() > c.getLeftValue()) // Celle jou�e est plus forte
				{
					c.getView().swapColor(); // On retourne l'autre
					swapped++;
				}
			}
		}
		else if (cell % 3 == 1) // Colonne du milieu
		{
			Card c = this.board[cell + 1];
			if (c != null) // Si il y a une carte a sa droite
			{
				if (c.getView().getColor() != player && card.getRightValue() > c.getLeftValue()) // Celle jou�e est plus forte
				{
					c.getView().swapColor(); // On retourne l'autre
					swapped++;
				}
			}
			
			c = this.board[cell - 1];
			if (c != null) // Si il y a une carte a sa gauche
			{
				if (c.getView().getColor() != player && card.getLeftValue() > c.getRightValue()) // Celle jou�e est plus forte
				{
					c.getView().swapColor(); // On retourne l'autre
					swapped++;
				}
			}
		}
		else // Colonne de droite
		{
			Card c = this.board[cell - 1];
			if (c != null) // Si il y a une carte a sa gauche
			{
				if (c.getView().getColor() != player && card.getLeftValue() > c.getRightValue()) // Celle jou�e est plus forte
				{
					c.getView().swapColor(); // On retourne l'autre
					swapped++;
				}
			}
		}
		
		if (cell / 3 == 0) // Ligne du haut
		{
			Card c = this.board[cell + 3];
			if (c != null) // Si il y a une carte en dessous
			{
				if (c.getView().getColor() != player && card.getBottomValue() > c.getTopValue()) // Celle jou�e est plus forte
				{
					c.getView().swapColor(); // On retourne l'autre
					swapped++;
				}
			}
		}
		else if (cell / 3 == 1) // Ligne du milieu
		{
			Card c = board[cell + 3];
			if (c != null) // Si il y a une carte en dessous
			{
				if (c.getView().getColor() != player && card.getBottomValue() > c.getTopValue()) // Celle jou�e est plus forte
				{
					c.getView().swapColor(); // On retourne l'autre
					swapped++;
				}
			}
			
			c = board[cell - 3];
			if (c != null) // Si il y a une carte en dessus
			{
				if (c.getView().getColor() != player && card.getTopValue() > c.getBottomValue()) // Celle jou�e est plus forte
				{
					c.getView().swapColor(); // On retourne l'autre
					swapped++;
				}
			}
		}
		else // Ligne du bas
		{
			Card c = this.board[cell - 3];
			if (c != null) // Si il y a une carte en dessus
			{
				if (c.getView().getColor() != player && card.getTopValue() > c.getBottomValue()) // Celle jou�e est plus forte
				{
					c.getView().swapColor(); // On retourne l'autre
					swapped++;
				}
			}
		}
		
		if (combo && swapped > 0)
			fireComboRuleTriggered();
	}
	// Applique la regle Identique sur le plateau
	private void applySameRule(int player, Card what, int cell, boolean combo)
	{
		ArrayList<Integer> cards = new ArrayList<Integer>(); // Cartes subissant/permettant Identique
		int carteAdverse = 0;
		boolean sameWall = false;
		
		if (cell - 3 >= 0 && this.board[cell - 3] != null) // carte du dessus si existante
		{
			if (this.board[cell - 3].getBottomValue() == what.getTopValue())
			{
				cards.add(cell - 3);
				if (this.board[cell - 3].getView().getColor() != player) carteAdverse += 1;
				if (Game.DEBUG) Log.d("SameRule", cell - 3 + " participe");
			}
		}
		else if (cell - 3 < 0 && regleMemeMur) // Regle MemeMur
		{
			if (10 == what.getTopValue())
			{
				cards.add(-1);
				sameWall = true;
				if (Game.DEBUG) Log.d("SameRule", cell - 3 + " participe");
			}
		}
		if (cell + 3 < this.board.length && this.board[cell + 3] != null) // carte du dessous si existante
		{
			if (this.board[cell + 3].getTopValue() == what.getBottomValue())
			{
				cards.add(cell + 3);
				if (this.board[cell + 3].getView().getColor() != player) carteAdverse += 1;
				if (Game.DEBUG) Log.d("SameRule", cell - 3 + " participe");
			}
		}
		else if (cell + 3 >= this.board.length && regleMemeMur) // Regle MemeMur
		{
			if (10 == what.getBottomValue())
			{
				cards.add(-1);
				sameWall = true;
				if (Game.DEBUG) Log.d("SameRule", cell + 3 + " participe");
			}
		}
		if (cell % 3 <= 1 && this.board[cell + 1] != null) // colonne gauche ou milieu
		{
			if (this.board[cell + 1].getLeftValue() == what.getRightValue())
			{
				cards.add(cell + 1);
				if (this.board[cell + 1].getView().getColor() != player) carteAdverse += 1;
				if (Game.DEBUG) Log.d("SameRule", cell + 1 + " participe");
			}
		}
		else if (cell % 3 == 2 && regleMemeMur) // Regle MemeMur
		{
			if (10 == what.getRightValue())
			{
				cards.add(-1);
				sameWall = true;
				if (Game.DEBUG) Log.d("SameRule", cell % 3 + " participe");
			}
		}
		if (cell % 3 >= 1 && this.board[cell - 1] != null) // colonne droite ou milieu
		{
			if (this.board[cell - 1].getRightValue() == what.getLeftValue())
			{
				cards.add(cell - 1);
				if (this.board[cell - 1].getView().getColor() != player) carteAdverse += 1;
				if (Game.DEBUG) Log.d("SameRule", cell % 3 + " participe");
			}
		}
		else if (cell % 3 == 0 && regleMemeMur) // Regle MemeMur
		{
			if (10 == what.getLeftValue())
			{
				cards.add(-1);
				sameWall = true;
				if (Game.DEBUG) Log.d("SameRule", cell % 3 + " participe");
			}
		}
		
		if (cards.size() >= 2 && carteAdverse >= 1)
		{
			if (Game.DEBUG) Log.d("SameRule", "ACTIVATED");
			ArrayList<Integer> swapped = new ArrayList<Integer>(); // Cartes swapped
			for (int c : cards) // Pour chaque carte, on les retourne si besoin est
			{
				if (c != -1)
				{
					Card card = this.board[c];
					if (card.getView().getColor() != player)
					{
						card.getView().swapColor();
						swapped.add(c);
					}
				}
			}
			
			try
			{ 
				if (!combo)
				{
					if (sameWall) 
						fireSameWallRuleTriggered();
					else 
						fireSameRuleTriggered();
				}
				else
					fireComboRuleTriggered();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
			
			if (regleCombo) // Si regle Combo
			{
				for (int c : swapped) // Pour chaque carte retourn�e, on fait suivre la combo
				{
					Card card = this.board[c];
					applySameRule(player, card, c, true);
					if (reglePlus) applyPlusRule(player, card, c, true);
					applyBasicRule(player, card, c, true);
				}
			}
		}
	}
	
	// Applique la regle Plus sur le plateau
	private void applyPlusRule(int player, Card what, int cell, boolean combo)
	{
		Card[] cards = new Card[4];
		int[] numeros = new int[4];
		ArrayList<Integer> swapped = new ArrayList<Integer>();
		
		numeros[0] = cell - 3;
		numeros[1] = cell - 1;
		numeros[2] = cell + 3;
		numeros[3] = cell + 1;
		if (cell - 3 >= 0) cards[0] = this.board[cell - 3];
		if (cell + 3 < this.board.length) cards[2] = this.board[cell + 3];
		if (cell % 3 <= 1) cards[3] = this.board[cell + 1];
		if (cell % 3 >= 1) cards[1] = this.board[cell - 1];
		
		for (int i = 0; i < 3; i ++)
		{ 
			boolean condition = false;
			if (cards[i] != null)
			{
				int somme = 0;
				if (i == 0) somme = what.getTopValue() + cards[i].getBottomValue();
				else if (i == 1) somme = what.getLeftValue() + cards[i].getRightValue();
				else if (i == 2) somme = what.getBottomValue() + cards[i].getTopValue();
				
				if (player != cards[i].getView().getColor()) condition = true;
				
				for (int j = i+1; j < 4; j++)
				{
					if (cards[j] != null)
					{
						int somme2 = 0;
						if (j == 3) somme2 = what.getRightValue() + cards[j].getLeftValue();
						else if (j == 1) somme2 = what.getLeftValue() + cards[j].getRightValue();
						else if (j == 2) somme2 = what.getBottomValue() + cards[j].getTopValue();
						
						if (player != cards[j].getView().getColor()) condition = true;
						
						if (somme == somme2 && condition) // Toutes les conditions remplies
						{
							if (cards[i].getView().getColor() != player)
							{
								cards[i].getView().swapColor();
								swapped.add(i);
							}
							if (cards[j].getView().getColor() != player) 
							{
								cards[j].getView().swapColor();
								swapped.add(j);
							}
						}
					}
				}
			}
		}
		
		try
		{
			if (swapped.size() >= 1)
			{
				if (combo) 
					fireComboRuleTriggered();
				else
					firePlusRuleTriggered();
			}
		}
		catch (Exception e)
		{
			return;
		}
		
		if (regleCombo)  // Si regle Combo
		{
			for (int i : swapped) // Pour chaque carte retourn�e, on fait suivre la combo
			{
				int c = numeros[i];
				Card card = cards[i];
				if (regleIdentique) applySameRule(player, card, c, true);
				applyPlusRule(player, card, c, true);
				applyBasicRule(player, card, c, true);
			}
		}
	}
	
	private void applyElementaireRule(Card card, int cell)
	{
		if (Game.DEBUG) Log.d("Elementaire", card.getElement() + " : " + boardElements[cell]);
		
		if (boardElements[cell] == null || boardElements[cell] == "")
			return;
		
		if (boardElements[cell].equals(card.getElement()))
			card.bonusElementaire();
		else
			card.malusElementaire();
	}
}
